/*
 * Copyright (C) 2022-2024 Huawei Device Co., Ltd.
 * Licensed under the MIT License, (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://opensource.org/licenses/MIT
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import LightWeightSet from '@ohos.util.LightWeightSet';
import LightWeightMap from '@ohos.util.LightWeightMap';
import { Subscription } from './Subscription';
import { References } from '../listener/References';
import HashMap from '@ohos.util.HashMap';
import { Utils } from '../utils/Utils';
import { SubscriptionFactory } from './SubscriptionFactory';
import { BusRuntime } from '../bus/BusRuntime';
import { MetadataReader } from '../listener/MetadataReader';
import { LogUtil } from '../../../../utils/LogUtil';

/**
 * 管理订阅
 */

export class SubscriptionManager {
  private static sInstance: SubscriptionManager;
  private referenceMap: HashMap<String, References>;
  private nonListeners: LightWeightSet<Object>;
  private subscriptionPerListener: LightWeightMap<String, Array<Subscription>>;
  private runtime: BusRuntime;
  private subscriptionFactory: SubscriptionFactory;
  private metadataReader: MetadataReader;
  private mClassName: String;

  constructor(metadataReader: MetadataReader, factory: SubscriptionFactory, runtime: BusRuntime) {
    LogUtil.info('SubscriptionManager initialized');
    this.referenceMap = new HashMap<String, References>();
    this.nonListeners = new LightWeightSet<Object>();
    this.subscriptionPerListener = new LightWeightMap<String, Array<Subscription>>();
    this.runtime = runtime;
    this.subscriptionFactory = factory;
    this.metadataReader = metadataReader;
  }

  public subscribe(listener: Object): void {
    try {
      //添加listener
      if (this.nonListeners.has(listener)) {
        return;
      }
      let className = Utils.getClassName(listener);
      this.mClassName = className;
      //根据listener获取sub对象
      let isStrong: boolean = this.metadataReader.useStrongReferences(className);
      let subscriptionArray = this.getSubscriptionsByListener(listener);
      if (!subscriptionArray) {
        let handleMethods = this.metadataReader.getMethodHandle(className);
        if (!handleMethods || handleMethods.length == 0) {
          this.nonListeners.add(listener);
          return;
        }
        subscriptionArray = new Array<Subscription>();
        handleMethods.forEach((parameter, index) => {
          subscriptionArray.push(this.subscriptionFactory.createSubscription(this.runtime, parameter, isStrong));
        })
        subscriptionArray.forEach((value, index) => {
          value.subscribe(listener);
        })
        this.subscriptionPerListener.set(className, subscriptionArray);
      } else {
        subscriptionArray.forEach((value, index) => {
          value.subscribe(listener);
        })
      }
    } catch (e) {
      LogUtil.error(e);
    }
  }

  public unsubscribe(listener: Object): boolean {
    if (!listener) {
      return false;
    }
    let subscriptions = this.getSubscriptionsByListener(listener);
    if (!subscriptions || subscriptions.length == 0) {
      return false;
    }
    let isRemoved: boolean = true;
    subscriptions.forEach((v, i) => {
      isRemoved = isRemoved && v.unsubscribe(listener);
    })
    return isRemoved;
  }

  public getSubscriptionsByListener(listener: Object): Array<Subscription> | undefined {
    if (!listener) {
      return undefined;
    }
    var className = Utils.getClassName(listener);
    try {
      if (this.subscriptionPerListener.hasKey(className)) {
        return this.subscriptionPerListener.get(className);
      }
    } catch (e) {
      LogUtil.error(e);
    }
    return undefined;
  }

  public getSubscriptionsByClassName(): Array<Subscription> | undefined {
    if (!this.mClassName) {
      return undefined;
    }
    try {
      if (this.subscriptionPerListener.hasKey(this.mClassName)) {
        return this.subscriptionPerListener.get(this.mClassName);
      }
    } catch (e) {
      LogUtil.error(e);
    }
    return undefined;
  }

  public setReferences(className: String, rType: References) {
    try {
      if (this.referenceMap) {
        this.referenceMap.set(className, rType);
      }
    } catch (e) {
      LogUtil.error(e);
    }
  }
}